﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace Weka
{
    internal class StreamWrita
    {
        private readonly Stream _stream;
        private byte[] _buffer = new byte[16];
        private int _bitsWritten;
        private byte _headerByte;
        public StreamWrita(Stream stream) { _stream = stream; }

        public void Write(MessageIdAttribute messageId)
        {
            _stream.WriteByte(messageId.Id);
        }

        internal void Write(FieldInfo fieldInfo)
        {
            fieldInfo.Write(_stream);
        }

        internal void Write(FieldAttribute field, DataType dataType)
        {
            _stream.WriteByte(field.Id);//todo
            _stream.WriteByte((byte)dataType);//todo
        }

        public void Write(byte v)
        {
            _stream.WriteByte(v);
        }

        public void Write(Int16 v)
        {
            Write(unchecked((UInt16)v));
        }

        public void Write(UInt16 v)
        {
            _stream.WriteByte((byte)(v >> 8));
            _stream.WriteByte((byte)v);
        }

        public void Write(Int32 v)
        {
            Write(unchecked((UInt32)v));
        }
        public void Write(UInt32 v)
        {
            _buffer[0] = (byte)(v >> 24);
            _buffer[1] = (byte)(v >> 16);
            _buffer[2] = (byte)(v >> 8);
            _buffer[3] = (byte)v;
            _stream.Write(_buffer, 0, 4);
        }
        public void Write(UInt64 v)
        {
            Write(unchecked((UInt32)(v >> 32)));
            Write(unchecked((UInt32)v));
        }
        public void Write(Int64 v)
        {
            Write(unchecked((UInt64)v));
        }

        internal void WriteStringRaw(string v, int byteCount)
        {
            if (_buffer.Length < byteCount) _buffer = new byte[byteCount /*+ (l >> 1)*/];
            UTF8Encoding.UTF8.GetBytes(v, 0, v.Length, _buffer, 0);
            _stream.Write(_buffer, 0, byteCount);
        }

        internal void WriteStringRaw(string v)
        {
            WriteStringRaw(v, UTF8Encoding.UTF8.GetByteCount(v));
        }

        public void HeaderCommit()
        {
            if (_bitsWritten == 0) return;

            _stream.WriteByte(_headerByte);
            _bitsWritten = 0;
            _headerByte = 0;
        }
        public void HeaderWrite0()
        {
            _bitsWritten++;

            if (_bitsWritten == 8)
                WriteAndResetHeader();
        }
        public void HeaderWrite1()
        {
            _headerByte = (byte)(_headerByte | (1 << _bitsWritten));
            HeaderWrite0();
        }

        private void WriteAndResetHeader()
        {
            _stream.WriteByte(_headerByte);
            _bitsWritten = 0;
            _headerByte = 0;
        }

        public void HeaderWrite00()
        {
            if (_bitsWritten < 6)
            {
                _bitsWritten += 2;
            }
            else
            {
                _stream.WriteByte(_headerByte);
                _bitsWritten = _bitsWritten - 6;
                _headerByte = 0;
            }
        }
        public void HeaderWrite01()
        {
            HeaderWrite1();
            HeaderWrite0();
        }
        public void HeaderWrite10()
        {
            HeaderWrite0();
            HeaderWrite1();
        }
        public void HeaderWrite11()
        {
            HeaderWrite1();
            HeaderWrite1();
        }




        internal void HeaderWrite4Bits(int bits4)
        {
            if (_bitsWritten <= 4)
            {
                _headerByte = (byte)(_headerByte | (bits4 << _bitsWritten));
                _bitsWritten += 4;

                if (_bitsWritten == 8)
                    WriteAndResetHeader();
            }
            else//5,6,7
            {
                int bitsLeftToWrite = _bitsWritten - 4;
                _headerByte = (byte)(_headerByte | (bits4 << _bitsWritten));
                WriteAndResetHeader();
                _headerByte = (byte)(bits4 >> (4 - bitsLeftToWrite));
                _bitsWritten = bitsLeftToWrite;
            }
        }

        internal void HeaderWrite1Bits(int bit)
        {
            _headerByte = (byte)(_headerByte | (bit << _bitsWritten));
            _bitsWritten++;

            if (_bitsWritten == 8)
                WriteAndResetHeader();
        }
    }
}
